#include "tracksmanager.h"
#include "configuration.h"

#include "controls/filebrowser.h"
#include "controls/ftwidgetlist.h"
#include "controls/ftmessagebox.h"
#include "controls/ftpushbutton.h"
#include "controls/ftvboxlayout.h"
#include "controls/fthboxlayout.h"
#include "controls/ftcombobox.h"
#include "controls/ftlabel.h"
#include "controls/ftlineedit.h"
#include "controls/ftradiobutton.h"
#include "controls/ftcheckbox.h"
#include "controls/ftcolordialog.h"

#include "tracksmanagerlabel.h"
#include "workers/workerloadgpx.h"

#include "statstracks.h"
#include "breadcrumbssync.h"
#include "gps_track.h"

TracksManager::TracksManager(const GpsManager* g) :
    RotatingWidget()
{
    gpsManager = g;
    isNavigating = navigationReverse = false;
    navigationTrack = navigationLeg = navigationPoint = -1;

    buildMain();
    buildEdit();
    buildBreadcrumbs();
    buildColors();
    buildnavigation();
}

TracksManager::~TracksManager()
{
    foreach (int id, tracks.keys() )
        delete tracks.take( id );

    foreach (QAbstractButton* btn, navigationMode.buttons() )
        navigationMode.removeButton( btn );
}

void TracksManager::buildMain()
{
    pages = new QStackedWidget();

    mainPage = new QWidget();
    pages->addWidget( mainPage );

    // Create the scroll widget list
    track_list_widget = new FTWidgetList();

    // Create buttons...
    import_track_button = new FTPushButton(  tr("import") );
    import_track_button->setIcon( QIcon(":/icons/import-track.png") );
    connect( import_track_button, SIGNAL(clicked()),
                      this, SLOT(importTrack()));

    load_track_button = new FTPushButton( tr("load") );
    load_track_button->setIcon( QIcon(":/icons/load-track.png") );
    load_track_button->setText( tr("load") );
    connect( load_track_button, SIGNAL(clicked()),
                        this, SLOT(loadTrack()));

    // Connect reorder&delete signals
    connect( track_list_widget, SIGNAL(orderChanged(QList<int>)),
                    this, SLOT(sortTracks(QList<int>)) );
    connect( track_list_widget, SIGNAL(itemDeleted(int)),
                    this, SLOT(removeTrack(int)) );
}

void TracksManager::buildEdit()
{
    selectedTrack = -1;

    editPage = new QWidget();
    pages->addWidget( editPage );

    stats = new StatsTracks();

    navigationButton = new FTPushButton( tr("Follow track...") );
    connect( navigationButton, SIGNAL(clicked()),
             this, SLOT(openNavigation()));

    breadcrumbsButton = new FTPushButton( tr("GoBreadcrumbs!") );
    connect( breadcrumbsButton, SIGNAL(clicked()),
             this, SLOT(openBreadcrumbs()) );

    colorsButton = new FTPushButton( tr("Set colors...") );
    connect( colorsButton, SIGNAL(clicked()),
             this, SLOT(openColors()) );

    closeEditButton = new FTPushButton( tr("Back to list.") );
    connect( closeEditButton, SIGNAL(clicked()),
             this, SLOT(closeEdit()) );

}

void TracksManager::buildBreadcrumbs()
{
    breadcrumbsPage = new QWidget();
    pages->addWidget( breadcrumbsPage );

    breadcrumbs = new BreadcrumbsSync();
    connect( breadcrumbs, SIGNAL(trackUploaded()),
             this, SLOT(closeBreadcrumbs()) );

    connect( breadcrumbs, SIGNAL(trackDownloaded()),
             this, SLOT(trackImported()));

    breadcrumbsCloseButton = new FTPushButton( tr("Close") );
    connect( breadcrumbsCloseButton, SIGNAL(clicked()),
             this, SLOT(closeBreadcrumbs()) );

}

void TracksManager::buildColors()
{
    selectedLeg = -1;
    colorsPage = new QWidget();
    pages->addWidget( colorsPage );

    colorsList = new FTComboBox();
    connect( colorsList, SIGNAL(selectedItem(int)),
             this, SLOT(colorsSelectLeg()) );

    closeColorsButton = new FTPushButton( tr("Close") );
    connect( closeColorsButton, SIGNAL(clicked()),
             this, SLOT(colorsModifyLeg()) );
    connect( closeColorsButton, SIGNAL(clicked()),
             this, SLOT(closeColors()) );

    colorDialog = new FTColorDialog();
}

void TracksManager::buildnavigation()
{
    navigationPage = new QWidget();
    pages->addWidget( navigationPage );

    navigationLegLabel = new FTLabel( tr("Select leg to navigation:") );

    navigationLegList = new FTComboBox();


    navigationBeginningRadio = new FTRadioButton( tr("From the start" ) );
    navigationClosestRadio = new FTRadioButton( tr("From closest point") );
    navigationMode.addButton( navigationBeginningRadio );
    navigationMode.addButton( navigationClosestRadio );

    navigationProximityLabel = new FTLabel( tr("Proximity threshold (mt):") );
    navigationProximityEdit = new FTLineEdit( QString("%1").arg( configuration->navigationProximity() ));

    navigationReverseButton = new FTCheckBox( tr("navigation backward") );
    navigationReverseButton->setChecked( false );

    startnavigationButton = new FTPushButton( tr("Start!") );
    connect( startnavigationButton, SIGNAL(clicked()),
             this, SLOT(startNavigation()) );

    closenavigationButton = new FTPushButton( tr("Close") );
    connect( closenavigationButton, SIGNAL(clicked()),
             this, SLOT(closeNavigation()) );

}

void TracksManager::sortTracks(QList<int> list )
{
    QHash<int, GPS_track*> new_order;
    // Iterate on all layers in list
    foreach (int id, list )
    {
        if ( tracks.contains( id ) )
            new_order.insert( id, tracks.take( id ) );
    }
    // and add tracks if any is left (should not, but you never know)
    foreach (int id, tracks.keys() )
        new_order.insert( id, tracks.take( id ) );
    tracks = new_order;
}

void TracksManager::removeTrack(int id)
{
    // Get the track with the given ID
    if ( tracks.contains( id ) )
        delete tracks.take( id );
}

void TracksManager::addTrack(const QString & filename)
{
    // Create new track...
    GPS_track* new_track = new GPS_track();
    // create the worker...
    WorkerLoadGpx runner( filename, new_track, tr("Loading GPX...") );
    // Load the GPX!
    if ( !runner.run() )
    {
        FTMessageBox::Error(tr("Loading track..."), tr("Unable to load GPX track!"));
        delete new_track;
    }
    else
        addTrack( new_track );
}

void TracksManager::addTrack(GPS_track* new_track)
{
    if ( new_track == NULL )
        return;
    int track_id = new_track->id();
    if ( tracks.contains( track_id ))
    {
        FTMessageBox::Error(tr("Loading track..."), tr("Track already loaded!") );
        return;
    }
    tracks.insert( track_id, new_track );
    TracksManagerLabel* lab = new TracksManagerLabel(new_track);
    track_list_widget->appendWidget( track_id, lab );
    connect( lab, SIGNAL(editTrack(int)),
             this, SLOT(openEdit(int)) );
    openEdit( selectedTrack );
}

QList<int> TracksManager::listTracks() const
{
    return tracks.keys();
}

const GPS_track* TracksManager::trackPtr(int i) const
{
    return tracks.value( i, NULL );
}

void TracksManager::loadTrack()
{
    QStringList filters;
    filters << "*gpx";
    QString filename = FileBrowser::browseFilename(FileBrowser::loadMode,
                                       tr("Load track..."),
                                       configuration->defaultPath(),
                                       filters);

    if ( filename != "" )
        addTrack( filename );
}

void TracksManager::importTrack()
{
    breadcrumbs->setMode( BreadcrumbsSync::download_track);
    pages->setCurrentWidget( breadcrumbsPage );
}

void TracksManager::trackImported()
{
    GPS_track* new_track = breadcrumbs->fetchDownloadedTrack();
    addTrack( new_track );
    openEdit( new_track->id() );
}

QLayout* TracksManager::createHorizontal()
{
    FTVBoxLayout* vgrid;
    FTHBoxLayout* hgrid;

    if ( mainPage->layout() != NULL )
        delete mainPage->layout();
    hgrid = new FTHBoxLayout();
    vgrid = new FTVBoxLayout();
    vgrid->addStretch();
    vgrid->addWidget( load_track_button );
    vgrid->addStretch();
    vgrid->addWidget( import_track_button );
    vgrid->addStretch();
    hgrid->addLayout( vgrid );
    hgrid->addWidget( track_list_widget, 2 );
    mainPage->setLayout( hgrid );

    if ( editPage->layout() != NULL )
        delete editPage->layout();
    hgrid = new FTHBoxLayout();
    vgrid = new FTVBoxLayout();
    vgrid->addStretch();
    vgrid->addWidget( breadcrumbsButton );
    vgrid->addStretch();
    vgrid->addWidget( colorsButton );
    vgrid->addStretch();
    vgrid->addWidget( closeEditButton );
    vgrid->addStretch();
    vgrid->addWidget( navigationButton );
    vgrid->addStretch();
    hgrid->addLayout( vgrid );
    hgrid->addWidget( stats, 2 );
    editPage->setLayout( hgrid );

   if ( breadcrumbsPage->layout() != NULL )
        delete breadcrumbsPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( breadcrumbs );
    vgrid->addWidget( breadcrumbsCloseButton );
    breadcrumbsPage->setLayout( vgrid );

    if ( colorsPage->layout() != NULL )
        delete colorsPage->layout();
    hgrid = new FTHBoxLayout();
    vgrid = new FTVBoxLayout();
    vgrid->addStretch();
    vgrid->addWidget( closeColorsButton );
    vgrid->addStretch();
    hgrid->addLayout( vgrid );
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( colorsList );
    vgrid->addWidget( colorDialog, 2 );
    hgrid->addLayout( vgrid, 2 );
    colorsPage->setLayout( hgrid );

    if ( navigationPage->layout() != NULL )
        delete navigationPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( navigationLegLabel );
    vgrid->addWidget( navigationLegList );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( navigationBeginningRadio );
    hgrid->addWidget( navigationClosestRadio );
    vgrid->addLayout( hgrid );
    vgrid->addWidget( navigationReverseButton );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( navigationProximityLabel );
    hgrid->addWidget( navigationProximityEdit );
    vgrid->addLayout( hgrid );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( startnavigationButton );
    hgrid->addWidget( closenavigationButton );
    vgrid->addStretch();
    vgrid->addLayout( hgrid );
    vgrid->addStretch();
    navigationPage->setLayout( vgrid );

    vgrid = new FTVBoxLayout();
    vgrid->addWidget( pages );
    return vgrid;
}

QLayout* TracksManager::createVertical()
{
    FTVBoxLayout* vgrid;
    FTHBoxLayout* hgrid;

    if ( mainPage->layout() != NULL )
        delete mainPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget(track_list_widget);
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( load_track_button );
    hgrid->addWidget( import_track_button );
    vgrid->addLayout( hgrid );
    mainPage->setLayout( vgrid );

    if ( editPage->layout() != NULL )
        delete editPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( stats );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( breadcrumbsButton );
    hgrid->addWidget( colorsButton );
    vgrid->addLayout( hgrid );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( closeEditButton );
    hgrid->addWidget( navigationButton );
    vgrid->addLayout( hgrid );
    editPage->setLayout( vgrid );

    if ( breadcrumbsPage->layout() != NULL )
        delete breadcrumbsPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( breadcrumbs );
    vgrid->addWidget( breadcrumbsCloseButton );
    breadcrumbsPage->setLayout( vgrid );

    if ( colorsPage->layout() != NULL )
        delete colorsPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( colorsList );
    vgrid->addWidget( colorDialog, 2 );
    vgrid->addWidget( closeColorsButton );
    colorsPage->setLayout( vgrid );

    if ( navigationPage->layout() != NULL )
        delete navigationPage->layout();
    vgrid = new FTVBoxLayout();
    vgrid->addWidget( navigationLegLabel );
    vgrid->addWidget( navigationLegList );
    vgrid->addWidget( navigationBeginningRadio );
    vgrid->addWidget( navigationClosestRadio );
    vgrid->addWidget( navigationReverseButton );
    vgrid->addWidget( navigationProximityLabel );
    vgrid->addWidget( navigationProximityEdit );
    hgrid = new FTHBoxLayout();
    hgrid->addWidget( startnavigationButton );
    hgrid->addWidget( closenavigationButton );
    vgrid->addStretch();
    vgrid->addLayout( hgrid );
    navigationPage->setLayout( vgrid );

    vgrid = new FTVBoxLayout();
    vgrid->addWidget( pages );
    return vgrid;
}

const GPS_track* TracksManager::getSelectedTrack()
{
    if ( tracks.contains( selectedTrack ) )
        return tracks.value( selectedTrack );
    return NULL;
}
const GPS_leg* TracksManager::getSelectedLeg()
{
    const GPS_leg* leg = NULL;
    const GPS_track* trk = getSelectedTrack();
    if ( trk != NULL )
        leg = trk->leg( selectedLeg );
    return leg;
}

void TracksManager::openEdit(int t)
{
    if ( tracks.contains( t ) )
    {
        selectedTrack = t;
        stats->setTrack( tracks.value( selectedTrack ) );
        pages->setCurrentWidget( editPage );
    }
}

void TracksManager::closeEdit()
{
    selectedTrack = -1;
    pages->setCurrentWidget( mainPage );
}

void TracksManager::colorsSelectLeg()
{
    int new_leg = colorsList->selectedItemId();
    if ( new_leg != selectedLeg )
    {
        // First, save old leg (if any)
        if ( selectedLeg != -1 )
            colorsModifyLeg();
        // Then load the new leg
        selectedLeg = new_leg;
        const GPS_leg* leg = getSelectedLeg();
        if ( leg != NULL )
        {
            colorDialog->setCurrentColor( leg->color() );
        }
    }
}

void TracksManager::colorsModifyLeg()
{
    if ( getSelectedLeg() != NULL )
    {
        GPS_leg* leg = tracks.value( selectedTrack )->legMod( selectedLeg );
        leg->setColor( colorDialog->currentColor() );
    }
}

void TracksManager::closeColors()
{
    pages->setCurrentWidget( editPage );
}

void TracksManager::openBreadcrumbs()
{
    if ( tracks.contains( selectedTrack ) )
    {
        breadcrumbs->setUploadTrack( tracks.value( selectedTrack ) );
        pages->setCurrentWidget( breadcrumbsPage );
    }
}

void TracksManager::openColors()
{
    if ( tracks.contains( selectedTrack ) )
    {
        colorsList->clear();
        const GPS_track* trk = getSelectedTrack();
        if ( trk != NULL )
        {
            foreach (int leg_id, trk->listLegs() )
            {
                const GPS_leg* leg = trk->leg( leg_id );
                colorsList->addItem( leg_id, leg->title() );
            }
            colorsList->selectFirst();
            colorsSelectLeg();
        }
        pages->setCurrentWidget( colorsPage );
    }
}

void TracksManager::openNavigation()
{
    if ( tracks.contains( selectedTrack ) )
    {
        navigationLegList->clear();
        const GPS_track* trk = getSelectedTrack();
        if ( trk != NULL )
        {
            foreach (int leg_id, trk->listLegs() )
            {
                const GPS_leg* leg = trk->leg( leg_id );
                navigationLegList->addItem( leg_id, leg->title() );
            }
            navigationLegList->selectFirst();
        }
        navigationBeginningRadio->setChecked(true);
        pages->setCurrentWidget( navigationPage );
    }
}
void TracksManager::closeBreadcrumbs()
{
    if ( selectedTrack != -1 )
        pages->setCurrentWidget( editPage );
    else
        pages->setCurrentWidget( mainPage );
}

void TracksManager::closeNavigation()
{
    pages->setCurrentWidget( editPage );
}

void TracksManager::stopNavigation()
{
    isNavigating = false;
    emit navigationStopped();
}

void TracksManager::destinationReached()
{
    if ( isNavigating )
    {
        const GPS_track* trk = tracks.value( navigationTrack, NULL );
        if ( trk != NULL )
        {
            const GPS_leg* leg = trk->leg( navigationLeg );
            int n_points = leg->nPoints();
            if ( n_points <= 0 )
                return;

            if ( !navigationReverse )
            {
                navigationPoint++;
                if ( navigationPoint >= n_points )
                    return;
            }
            else
            {
                navigationPoint--;
                if ( navigationPoint < 0 )
                    return;
            }
            const GPS_point* pt = leg->point( navigationPoint );
            emit navigateTo( *pt );
        }
        else
        {
            FTMessageBox::Error( tr("navigation Track"), tr("An error has occourred while following the track!"));
            stopNavigation();
        }
    }
}
void TracksManager::startNavigation()
{
    if ( getSelectedTrack() == NULL )
        return;

    if ( isNavigating )
    {
        if ( !FTMessageBox::Yesno( tr("navigation Track"),
                                  tr("You are already following a track! Continue?")) )
            return;
    }
    stopNavigation();

    navigationTrack = selectedTrack;
    navigationLeg = navigationLegList->selectedItemId();
    navigationReverse = navigationReverseButton->isChecked();

    configuration->setNavigationProximity( navigationProximityEdit->text().toInt() );

    const GPS_leg* leg = getSelectedTrack()->leg( navigationLeg );
    int n_points = leg->nPoints();
    if ( n_points <= 0 )
    {
        FTMessageBox::Error( tr("navigation Track"), tr("Leg is empty!") );
        return;
    }

    if ( navigationBeginningRadio->isChecked() )
    {
        if ( !navigationReverse )
            navigationPoint = 0;
        else
            navigationPoint = n_points-1;
    }
    else
    {
        double minimum_distance = 9999.9;
        navigationPoint = -1;
        GPS_point current_position = gpsManager->currentPosition();
        for (int p = 0; p < n_points; p++ )
        {
            const GPS_point* lpt = leg->point(p);
            double distance = lpt->distanceTo( current_position );
            if ( (distance < minimum_distance) || (navigationPoint == -1) )
            {
                navigationPoint = p;
                minimum_distance = distance;
            }
        }
    }

    const GPS_point* pt = leg->point( navigationPoint );
    emit navigateTo( *pt );
    isNavigating = true;
    closeNavigation();
}

